#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
#define _CRT_SECURE_NO_WARNINGS
#endif

#include "sweety.h"
#ifndef SW_DISABLE

#ifndef SW_DEFINE_MATH_OPERATORS
#define SW_DEFINE_MATH_OPERATORS
#endif
#include "sweety_Internal.h"

#include "sweety_3D_Engine.h"

// System includes
#include <ctype.h>      // toupper
#if defined(_MSC_VER) && _MSC_VER <= 1500 // MSVC 2008 or earlier
#include <stddef.h>     // intptr_t
#else
#include <stdint.h>     // intptr_t
#endif

//-------------------------------------------------------------------------
// Warnings
//-------------------------------------------------------------------------

// Visual Studio warnings
#ifdef _MSC_VER
#pragma warning (disable: 4127)     // condition expression is constant
#pragma warning (disable: 4996)     // 'This function or variable may be unsafe': strcpy, strdup, sprintf, vsnprintf, sscanf, fopen
#if defined(_MSC_VER) && _MSC_VER >= 1922 // MSVC 2019 16.2 or later
#pragma warning (disable: 5054)     // operator '|': deprecated between enumerations of different types
#endif
#endif

// Clang/GCC warnings with -Weverything
#if defined(__clang__)
#if __has_warning("-Wunknown-warning-option")
#pragma clang diagnostic ignored "-Wunknown-warning-option"         // warning: unknown warning group 'xxx'                      // not all warnings are known by all Clang versions and they tend to be rename-happy.. so ignoring warnings triggers new warnings on some configuration. Great!
#endif
#pragma clang diagnostic ignored "-Wunknown-pragmas"                // warning: unknown warning group 'xxx'
#pragma clang diagnostic ignored "-Wold-style-cast"                 // warning: use of old-style cast                            // yes, they are more terse.
#pragma clang diagnostic ignored "-Wfloat-equal"                    // warning: comparing floating point with == or != is unsafe // storing and comparing against same constants (typically 0.0f) is ok.
#pragma clang diagnostic ignored "-Wformat-nonliteral"              // warning: format string is not a string literal            // passing non-literal to vsnformat(). yes, user passing incorrect format strings can crash the code.
#pragma clang diagnostic ignored "-Wsign-conversion"                // warning: implicit conversion changes signedness
#pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant"  // warning: zero as null pointer constant                    // some standard header variations use #define NULL 0
#pragma clang diagnostic ignored "-Wdouble-promotion"               // warning: implicit conversion from 'float' to 'double' when passing argument to function  // using printf() is a misery with this as C++ va_arg ellipsis changes float to double.
#pragma clang diagnostic ignored "-Wenum-enum-conversion"           // warning: bitwise operation between different enumeration types ('XXXFlags_' and 'XXXFlagsPrivate_')
#pragma clang diagnostic ignored "-Wdeprecated-enum-enum-conversion"// warning: bitwise operation between different enumeration types ('XXXFlags_' and 'XXXFlagsPrivate_') is deprecated
#pragma clang diagnostic ignored "-Wimplicit-int-float-conversion"  // warning: implicit conversion from 'xxx' to 'float' may lose precision
#elif defined(__GNUC__)
#pragma GCC diagnostic ignored "-Wpragmas"                          // warning: unknown option after '#pragma GCC diagnostic' kind
#pragma GCC diagnostic ignored "-Wformat-nonliteral"                // warning: format not a string literal, format string not checked
#pragma GCC diagnostic ignored "-Wclass-memaccess"                  // [__GNUC__ >= 8] warning: 'memset/memcpy' clearing/writing an object of type 'xxxx' with no trivial copy-assignment; use assignment or value-initialization instead
#endif



struct SwGuiSphere
{
    char* Name;
    float Size;
    SwVec2 controlPos;
    int TestFactor;
    quat qtV;
    SwU32 sphereColors[2];
    bool IsBuilded;
    bool IsNeedRebuild;

    SwVector<vec3> SphereVtx;
    SwVector<int>  SphereTess;

    SwGuiSphere()
    {
        Name = "";
        Size = .0f;
        qtV = quat(1.0f, vec3(0.0f)); // Quaternion value
        //sphereColors[2] = { 0xff401010, 0xffc0a0a0 }; // Tessellation colors
        IsBuilded = false;
        IsNeedRebuild = false;
    }
};


#define swGizmo_INTERPOLATE_NORMALS
#define STARTING_ALPHA_PLANE .75f

quat qtV = quat(1.0f, vec3(0.0f)); // Quaternion value
quat qtV2 = quat(1.0f, vec3(0.0f)); // Quaternion value
quat qtVOrigin = quat(1.0f, vec3(0.0f)); // Quaternion value
//quat qtV2 = quat(1.0f, vec3(0.0f)); // Quaternion value
SwU32 sphereColors[2] = { 0xff401010, 0xffc0a0a0 }; // Tessellation colors
float solidResizeFactor = 1.0f;
float sphereRadius = 1.27f;

// Axes resize
///////////////////////////////////////
vec3 axesResizeFactor(.95f, 1.0f, 1.0f);
vec3 savedAxesResizeFactor = axesResizeFactor;

// Plane color
///////////////////////////////////////
SwVec4 planeColor(0.0f, 0.5f, 1.0f, STARTING_ALPHA_PLANE);
SwVec4 savedPlaneColor = planeColor;

// Cube components
///////////////////////////////////////
float cubeSize = 1.05f;

// Plane components
///////////////////////////////////////
float planeSize = .33f;
float planeThickness = .015f;

// arrow/axes components
///////////////////////////////////////
int   coneSlices = 4;
float coneRadius = 1.07f;
float coneLength = 1.37f;

int   cylSlices = 7;
float cylRadius = 0.02f; // sizeCylLength = defined in base to control size

// Direction arrow color
///////////////////////////////////////
SwVec4 directionColor(1.0f, 1.0f, 0.0f, 1.0f);
SwVec4 savedDirectionColor = directionColor;

// Gizmo mouse settings
///////////////////////////////////////
float gizmoFeelingRot = 1.f; // >1 more mouse sensibility, <1 less mouse sensibility


SwVector<vec3> sphereVtx;
SwVector<int>  sphereTess;
SwVector<vec3> arrowVtx[4];
SwVector<vec3> arrowNorm[4];
SwVector<vec3> cubeVtx;
SwVector<vec3> cubeNorm;
SwVector<vec3> planeVtx;
SwVector<vec3> planeNorm;

enum {
    TYPE_Sphere,
    TYPE_Cone,
    TYPE_Cube
};

enum {                            
    mode3Axes = 0x0001, //0b0000'0001, 
    modeDirection = 0x0002, //0b0000'0010,
    modeDirPlane = 0x0004, //0b0000'0100,
    modeDual = 0x0008, //0b0000'1000,
    modePanDolly = 0x0010, //0b0001'0000,
    modeMask = 0x00ff,


    cubeAtOrigin = 0x0100, //0b0000'0000, 
    sphereAtOrigin = 0x0200, //0b0001'0000,
    noSolidAtOrigin = 0x0400, //0b0010'0000,
    modeFullAxes = 0x0800,
    axesModeMask = 0xff00
};

enum { sphereTess16, sphereTess8, sphereTess4, sphereTess2 };
enum { CONE_SURF, CONE_CAP, CYL_SURF, CYL_CAP };

enum { axisIsX, axisIsY, axisIsZ };
enum solidSides { backSide, frontSide };

int axesOriginType = cubeAtOrigin;
int drawMode = mode3Axes;
bool showFullAxes = false;

float dollyScale = 1.f, panScale = 1.f;
vgModifiers panMod = vg::evControlModifier, dollyMod = vg::evShiftModifier;

//  inline helper drawing functions
////////////////////////////////////////////////////////////////////////////
typedef vec3& (*ptrFunc)(vec3&);

inline vec3& AdjustPlane(vec3& coord)
{
    coord.x = (coord.x > 0.0f) ? (2.5f * coord.x - 1.6f) : coord.x;
    coord.x = (coord.x) * .5f + .5f + (coord.x > 0 ? -planeThickness : planeThickness) * solidResizeFactor;
    coord *= vec3(1.0f, 2.0f, 2.0f);
    return coord;
}

inline vec3& AdjustDir(vec3& coord)
{
    coord.x = (coord.x > 0.0f) ? (2.5f * coord.x - 1.6f) : coord.x + 0.1f;
    coord *= vec3(1.0f, 3.0f, 3.0f);
    return coord;
}

inline vec3& AdjustSpotCyl(vec3& coord)
{
    const float halfCylMinusCone = 1.0f - coneLength;
    coord.x = (coord.x * .075f - 2.0f + (halfCylMinusCone - halfCylMinusCone * .075f)); //cyl begin where cone end
    return coord;

}
inline vec3& AdjustSpotCone(vec3& coord)
{
    coord.x -= 2.00f;
    return coord;
}

inline vec3 FastRotate(int axis, vec3& v)
{
    return ((axis == axisIsY) ? vec3(-v.y, v.x, v.z) : // rotation Z 90'
        ((axis == axisIsZ) ? vec3(-v.z, v.y, v.x) : // rotation Y 90'
            v));
}

void ModeSettings(int mode) {
    drawMode = int(mode & modeMask); axesOriginType = int(mode & axesModeMask); showFullAxes = bool(modeFullAxes & mode);
}

//  Polygon
////////////////////////////////////////////////////////////////////////////
void BuildPolygon(const vec3& size, SwVector<vec3>& vtx, SwVector<vec3>& norm)
{

    vtx.clear();
    norm.clear();

#define V(a,b,c) vtx.push_back(vec3(a size.x, b size.y, c size.z))
#define N(x,y,z) norm.push_back(vec3(x, y, z))

    N(1.0f, 0.0f, 0.0f); V(+, -, +); V(+, -, -); V(+, +, -); V(+, +, +);
    N(0.0f, 1.0f, 0.0f); V(+, +, +); V(+, +, -); V(-, +, -); V(-, +, +);
    N(0.0f, 0.0f, 1.0f); V(+, +, +); V(-, +, +); V(-, -, +); V(+, -, +);
    N(-1.0f, 0.0f, 0.0f); V(-, -, +); V(-, +, +); V(-, +, -); V(-, -, -);
    N(0.0f, -1.0f, 0.0f); V(-, -, +); V(-, -, -); V(+, -, -); V(+, -, +);
    N(0.0f, 0.0f, -1.0f); V(-, -, -); V(-, +, -); V(+, +, -); V(+, -, -);

#undef V
#undef N
}

void BuildCube(const float size)
{
    BuildPolygon(vec3(size), cubeVtx, cubeNorm);
}

void BuildPlane(const float size, const float thickness = planeThickness)
{
    BuildPolygon(vec3(thickness, size, size), planeVtx, planeNorm);
}

//  Sphere
////////////////////////////////////////////////////////////////////////////
void BuildSphere(const float radius, const int tessFactor)
{
    const int div = tessFactor; //tessellation colors: meridians/div x paralles/div
    const int meridians = 32; //64/2;
    const int parallels = meridians / 2;

    sphereVtx.clear();
    sphereTess.clear();

#   define V(x,y,z) sphereVtx.push_back(vec3(x, y, z))
#   define T(t)     sphereTess.push_back(t)

    const float incAngle = 2.0f * T_PI / (float)(meridians);
    float angle = incAngle;

    // Adjust z and radius as stacks are drawn.
    float z0, z1 = cosf(angle) * radius;
    float r0, r1 = sinf(angle) * radius;
    float x1 = -1.0f;
    float y1 = 0.0f;

    // The first pole==>parallel is covered with triangles
    for (int j = 0; j < meridians; j++, angle += incAngle) {
        const float x0 = x1; x1 = cosf(T_PI - angle);
        const float y0 = y1; y1 = sinf(T_PI - angle);

        const int tType = ((j >> div) & 1);

        V(0.0f, 0.0f, radius); T(tType);
        V(x0 * r1, -y0 * r1, z1); T(tType);
        V(x1 * r1, -y1 * r1, z1); T(tType);
    }

    // Cover each stack with a quad divided in 2 triangles, except the top and bottom stacks 
    angle = incAngle + incAngle;
    x1 = 1.f; y1 = 0.f;

    for (int i = 1; i < parallels - 1; i++, angle += incAngle) {
        //int div =8;
        z0 = z1; z1 = cosf(angle) * radius;
        r0 = r1; r1 = sinf(angle) * radius;
        float angleJ = incAngle;

        for (int j = 0; j < meridians; j++, angleJ += incAngle) {
            const float x0 = x1; x1 = cosf(angleJ);
            const float y0 = y1; y1 = sinf(angleJ);

            const int tType = ((i >> div) & 1) ? ((j >> div) & 1) : !((j >> div) & 1);

            V(x0 * r1, -y0 * r1, z1); T(tType);
            V(x0 * r0, -y0 * r0, z0); T(tType);
            V(x1 * r0, -y1 * r0, z0); T(tType);
            V(x0 * r1, -y0 * r1, z1); T(tType);
            V(x1 * r0, -y1 * r0, z0); T(tType);
            V(x1 * r1, -y1 * r1, z1); T(tType);
        }
    }

    // The last parallel==>pole is covered with triangls
    z0 = z1;
    r0 = r1;
    x1 = -1.0f; y1 = 0.f;

    angle = incAngle;
    for (int j = 0; j < meridians; j++, angle += incAngle) {
        const float x0 = x1; x1 = cosf(angle + T_PI);
        const float y0 = y1; y1 = sinf(angle + T_PI);

        const int tType = ((parallels - 1) >> div) & 1 ? ((j >> div) & 1) : !((j >> div) & 1);

        V(0.0f, 0.0f, -radius); T(tType);
        V(x0 * r0, -y0 * r0, z0); T(tType);
        V(x1 * r0, -y1 * r0, z0); T(tType);
    }
#   undef V
#   undef C
}

//  Cone / Pyramid
////////////////////////////////////////////////////////////////////////////
void BuildCone(const float x0, const float x1, const float radius, const int slices)
{
    const float height = x1 - x0;

    // Scaling factors for vertex normals 
    const float sq = sqrtf(height * height + radius * radius);
    const float cosn = height / sq;
    const float sinn = radius / sq;

    const float incAngle = 2.0f * T_PI / (float)(slices);
    float angle = incAngle;

    float yt1 = sinn, y1 = radius;// ==> yt1 = cos(0) * sinn, y1 = cos(0) * radius 
    float zt1 = 0.0f, z1 = 0.0f;  // ==> zt1 = sin(0) * sinn, z1 = sin(0) * radius 

    const float xt0 = x0 * cosn, xt1 = x1 * cosn;

    arrowVtx[CONE_CAP].clear(); arrowNorm[CONE_CAP].clear();
    arrowVtx[CONE_SURF].clear(); arrowNorm[CONE_SURF].clear();

#   define V(i,x,y,z) arrowVtx [i].push_back(vec3(x, y, z))
#   define N(i,x,y,z) arrowNorm[i].push_back(vec3(x, y, z)) 

    for (int j = 0; j < slices; j++, angle += incAngle) {
        const float yt0 = yt1;  yt1 = cosf(angle);
        const float y0 = y1;   y1 = yt1 * radius;   yt1 *= sinn;
        const float zt0 = zt1;  zt1 = sinf(angle);
        const float z0 = z1;   z1 = zt1 * radius;   zt1 *= sinn;

        // Cover the circular base with a triangle fan... 
        V(CONE_CAP, x0, 0.f, 0.f);
        V(CONE_CAP, x0, y0, -z0);
        V(CONE_CAP, x0, y1, -z1);

        N(CONE_CAP, -1.f, 0.f, 0.f);
#                                    ifdef imguiGizmo_INTERPOLATE_NORMALS
        N(CONE_CAP, -1.f, 0.f, 0.f);
        N(CONE_CAP, -1.f, 0.f, 0.f);
#endif
        V(CONE_SURF, x1, 0.f, 0.f);
        V(CONE_SURF, x0, y0, z0);
        V(CONE_SURF, x0, y1, z1);
#                                    ifdef imguiGizmo_INTERPOLATE_NORMALS
        N(CONE_SURF, xt1, 0.f, 0.f);
        N(CONE_SURF, xt0, yt0, zt0);
        N(CONE_SURF, xt0, yt1, zt1);
#else
        N(CONE_SURF, xt0, yt0, zt0);
#endif
    }
#undef V
#undef N
}

//  Cylinder
////////////////////////////////////////////////////////////////////////////
void BuildCylinder(const float x0, const float x1, const float radius, const int slices)
{

    float y1 = 1.0f, yr1 = radius;
    float z1 = 0.0f, zr1 = 0.0f; // * radius

    const float incAngle = 2.0f * T_PI / (float)(slices);
    float angle = incAngle;

    arrowVtx[CYL_CAP].clear(); arrowNorm[CYL_CAP].clear();
    arrowVtx[CYL_SURF].clear(); arrowNorm[CYL_SURF].clear();

#   define V(i,x,y,z) arrowVtx [i].push_back(vec3(x, y, z))
#   define N(i,x,y,z) arrowNorm[i].push_back(vec3(x, y, z)) 

    for (int j = 0; j < slices; j++, angle += incAngle) {
        const float y0 = y1;   y1 = cosf(angle);
        const float z0 = z1;   z1 = sinf(angle);
        const float yr0 = yr1;  yr1 = y1 * radius;
        const float zr0 = zr1;  zr1 = z1 * radius;

        // Cover the base  
        V(CYL_CAP, x0, 0.f, 0.f);
        V(CYL_CAP, x0, yr0, -zr0);
        V(CYL_CAP, x0, yr1, -zr1);

        N(CYL_CAP, -1.f, 0.f, 0.f);
#                                   ifdef imguiGizmo_INTERPOLATE_NORMALS
        N(CYL_CAP, -1.f, 0.f, 0.f);
        N(CYL_CAP, -1.f, 0.f, 0.f);
#endif
        // Cover surface
        N(CYL_SURF, 0.f, y0, z0);
        N(CYL_SURF, 0.f, y0, z0);
#                                   ifdef imguiGizmo_INTERPOLATE_NORMALS
        N(CYL_SURF, 0.f, y1, z1);
        N(CYL_SURF, 0.f, y0, z0);
        N(CYL_SURF, 0.f, y1, z1);
        N(CYL_SURF, 0.f, y1, z1);
#endif
        V(CYL_SURF, x1, yr0, zr0);
        V(CYL_SURF, x0, yr0, zr0);
        V(CYL_SURF, x0, yr1, zr1);
        V(CYL_SURF, x1, yr0, zr0);
        V(CYL_SURF, x0, yr1, zr1);
        V(CYL_SURF, x1, yr1, zr1);
#ifdef SHOW_FULL_CYLINDER 
        // Cover the top ..in the arrow this cap is covered from cone/pyramid
        V(CYL_CAP, x1, 0.f, 0.f);
        V(CYL_CAP, x1, yr0, zr0);
        V(CYL_CAP, x1, yr1, zr1);
        N(CYL_CAP, 1.f, 0.f, 0.f);
#                               ifdef imguiGizmo_INTERPOLATE_NORMALS
        N(CYL_CAP, 1.f, 0.f, 0.f);
        N(CYL_CAP, 1.f, 0.f, 0.f);
#endif
#endif
    }
#undef V
#undef N
}

static inline int Clamp(int v, int mn, int mx)
{
    return (v < mn) ? mn : (v > mx) ? mx : v;
}

//
//  LightEffect
//      faster but minus cute/precise.. ok for sphere
////////////////////////////////////////////////////////////////////////////
inline SwU32 AddLightEffect(SwU32 color, float light)
{
    float l = ((light < .6f) ? .6f : light) * .8f;
    float lc = light * 80.0f;                    // ambient component 
    return   Clamp(SwU32(((color & 0xff) * l + lc)), 0, 255) |
        (Clamp(SwU32((((color >> 8) & 0xff) * l + lc)), 0, 255) << 8) |
        (Clamp(SwU32((((color >> 16) & 0xff) * l + lc)), 0, 255) << 16) |
        (SwU32(SW::GetStyle().Alpha * (color >> 24)) << 24);
}

//
//  LightEffect
//      with distance attenuatin
////////////////////////////////////////////////////////////////////////////
inline SwU32 AddLightEffect(const vec4& color, float light, float atten)
{
    vec3 l((light < .5) ? .5f : light);
    vec3 a(atten > .25 ? .25f : atten);
    vec3 c(((vec3(color) + l * .5f) * l) * .75f + a * vec3(color) * .45f + a * .25f);

    const float alpha = color.a * SW::GetStyle().Alpha; //SW::GetCo(SwGuiCol_FrameBg).w;
    return SW::ColorConvertFloat4ToU32(SwVec4(c.x, c.y, c.z, alpha));
}

inline SwU32 AddLightEffect(SwU32 color, float light, float atten)
{
    vec4 c(float(color & 0xff) / 255.f, float((color >> 8) & 0xff) / 255.f, float((color >> 16) & 0xff) / 255.f, 1.0f);
    return AddLightEffect(c, light, atten);
}

static void Add3dImpl(SwDrawList* draw_list, const char* name, const float size, const SwVec2& pos, SwVec4& axes, const int type)
{
    qtV.x = axes.x; qtV.y = axes.y; qtV.z = axes.z; qtV.w = axes.w;

    SwGuiIO& io = SW::GetIO();
    SwGuiStyle& style = SW::GetStyle();

    SW::PushID("##RotA");
    SW::BeginGroup();

    bool value_changed = false;
    SwVec2 controlPos(SW::GetCursorScreenPos());
    controlPos += pos;
    SW::SetCursorScreenPos(controlPos);

    const float squareSize = size;
    const float halfSquareSize = squareSize * .5;
    const SwVec2 innerSize(squareSize, squareSize);

    bool highlighted = false;
    SW::InvisibleButton(name, innerSize);

    vgModifiers vgMods = vg::evNoModifier;

    vg::vImGuIZMO track;
    //  getTrackball
    //      in : q -> quaternion to which applay rotations
    //      out: q -> quaternion with rotations
    ////////////////////////////////////////////////////////////////////////////
    auto getTrackball = [&](quat& q) {
        SwVec2 mouse = SW::GetMousePos() - controlPos;

        track.viewportSize(innerSize.x, innerSize.y);
        track.setRotation(q);
        track.setGizmoFeeling(gizmoFeelingRot);
        track.imGuIZMO_BASE_CLASS::motionImmediateMode(mouse.x, mouse.y, io.MouseDelta.x, io.MouseDelta.y, vgMods);
        q = track.getRotation();
        value_changed = true; // if getTrackball() called, value is changed
    };

    // LeftClick
    if (SW::IsItemActive())
    {
        highlighted = true;
        if (SW::IsMouseDragging(0))
        {
            getTrackball(qtV);
        }

        SwColor col(style.Colors[SwGuiCol_FrameBgActive]);
        col.Value.w *= SW::GetStyle().Alpha;
        draw_list->AddRectFilled(SwVec2(controlPos.x - 20, controlPos.y - 20), controlPos + SwVec2(innerSize.x + 20, innerSize.y + 20), col, style.FrameRounding);
    }

    //draw_list->PushClipRect(SwVec2(controlPos.x - 20, controlPos.y - 20), controlPos + SwVec2(innerSize.x + 20, innerSize.y + 20), false);

    const SwVec2 wpUV = SW::GetFontTexUvWhitePixel(); //culling versus
    SwVec2 uv[4]; SwU32 col[4]; //buffers to storetransformed vtx & col for PrimVtx & PrimQuadUV
    quat _q(normalize(qtV));

    //  Just a "few" lambdas... 
    //////////////////////////////////////////////////////////////////
    auto normalizeToControlSize = [&](float x, float y) {
        return controlPos + SwVec2(x, -y) * halfSquareSize + SwVec2(halfSquareSize, halfSquareSize); //drawing from 0,0 .. no borders
    };

    //////////////////////////////////////////////////////////////////
    auto addTriangle = [&]()
    {   // test cull dir        
        if (cross(vec2(uv[1].x - uv[0].x, uv[1].y - uv[0].y),
            vec2(uv[2].x - uv[0].x, uv[2].y - uv[0].y)) > 0) {
            uv[1] = uv[2] = uv[0];
        }

        for (int i = 0; i < 3; i++) draw_list->PrimVtx(uv[i], wpUV, col[i]);
    };

    //////////////////////////////////////////////////////////////////
    auto addQuad = [&](SwU32 colLight)
    {   // test cull dir
        if (cross(vec2(uv[1].x - uv[0].x, uv[1].y - uv[0].y),
            vec2(uv[3].x - uv[0].x, uv[3].y - uv[0].y)) > 0) {
            uv[3] = uv[1] = uv[2] = uv[0];
        }

        draw_list->PrimQuadUV(uv[0], uv[1], uv[2], uv[3], wpUV, wpUV, wpUV, wpUV, colLight);
    };

    //////////////////////////////////////////////////////////////////
    auto drawSphere = [&]()
    {
        draw_list->PrimReserve(sphereVtx.size(), sphereVtx.size()); // num vert/indices 
        auto itTess = sphereTess.begin();
        for (auto itVtx = sphereVtx.begin(); itVtx != sphereVtx.end(); ) {
            for (int h = 0; h < 3; h++, itTess++) {
                vec3 coord = _q * (*itVtx++ * solidResizeFactor);        //Rotate

                uv[h] = normalizeToControlSize(coord.x, coord.y);
                const float drawSize = sphereRadius * solidResizeFactor;
                col[h] = AddLightEffect(sphereColors[*itTess], (-drawSize * .5f + (coord.z * coord.z) / (drawSize * drawSize)));
            }
            addTriangle();
        }
    };

    //////////////////////////////////////////////////////////////////
    auto drawCube = [&]()
    {
        draw_list->PrimReserve(cubeNorm.size() * 6, cubeNorm.size() * 4); // num vert/indices 
        for (vec3* itNorm = cubeNorm.begin(), *itVtx = cubeVtx.begin(); itNorm != cubeNorm.end();) {
            vec3 coord;
            vec3 norm = _q * *itNorm;
            for (int i = 0; i < 4; ) {
                coord = _q * (*itVtx++ * solidResizeFactor);
                uv[i++] = normalizeToControlSize(coord.x, coord.y);
            }
            addQuad(AddLightEffect(vec4(abs(*itNorm++), 1.0f), norm.z, coord.z));
        }
    };


    //////////////////////////////////////////////////////////////////
    auto drawComponent = [&](const int idx, const quat& q, ptrFunc func)
    {
        auto* ptrVtx = arrowVtx + idx;
        draw_list->PrimReserve(ptrVtx->size(), ptrVtx->size()); // reserve vtx
        for (auto itVtx = ptrVtx->begin(), itNorm = (arrowNorm + idx)->begin(); itVtx != ptrVtx->end(); ) {
#if !defined(swGizmo_INTERPOLATE_NORMALS)
            vec3 norm = (_q * *itNorm++);
#endif
            for (int h = 0; h < 3; h++) {
                vec3 coord = *itVtx++;
#ifdef swGizmo_INTERPOLATE_NORMALS
                vec3 norm = (q * *itNorm++);
#endif
                //coord = q * (func(coord) * resizeAxes); // remodelling Directional Arrow (func) and transforms;
                coord = q * (func(coord)); // remodelling Directional Arrow (func) and transforms;

                uv[h] = normalizeToControlSize(coord.x, coord.y);
                col[h] = AddLightEffect(vec4(directionColor.x, directionColor.y, directionColor.z, 1.0), norm.z, coord.z > 0 ? coord.z : coord.z * .5);
            }
            addTriangle();
        }
    };

    //////////////////////////////////////////////////////////////////
    auto drawCone = [&](const quat& q, const float arrowCoordZ)
    {
        if (arrowCoordZ > 0) {
            drawComponent(CONE_SURF, q, AdjustSpotCone); drawComponent(CONE_CAP, q, AdjustSpotCone);
            drawComponent(CYL_SURF, q, AdjustSpotCyl); drawComponent(CYL_CAP, q, AdjustSpotCyl);
        }
        else {
            drawComponent(CYL_CAP, q, AdjustSpotCyl); drawComponent(CYL_SURF, q, AdjustSpotCyl);
            drawComponent(CONE_CAP, q, AdjustSpotCone); drawComponent(CONE_SURF, q, AdjustSpotCone);
        }
    };


    switch (type)
    {
    case TYPE_Sphere:
        drawSphere();
        break;
    case TYPE_Cube:
        drawCube();
        break;
    case TYPE_Cone:
        vec3 spot(qtV * vec3(-1.0f, 0.0f, .0f)); // versus opposite
        drawCone(normalize(qtV), spot.z);
        break;
    default:
        break;
    }
   

    // Draw text from top left corner
    SW::SetCursorScreenPos(controlPos);

    //draw_list->PopClipRect();
    SW::EndGroup();
    SW::PopID();

    if (value_changed)
    {
        axes.x = qtV.x; axes.y = qtV.y; axes.z = qtV.z; axes.w = qtV.w;
    }
}

void SwDrawList::AddSphere(const char* name, const float size, const SwVec2& pos, SwVec4& axes, const int tessFactor)
{
    SW_ASSERT(name);

    static bool isBuildSphere = false;
    if (!isBuildSphere)
    {
        BuildSphere(1.27, tessFactor);
        isBuildSphere = true;
    }

    Add3dImpl(this, name, size, pos, axes, TYPE_Sphere);
}

void SwDrawList::AddCube(const char* name, const float size, const SwVec2& pos, SwVec4& axes)
{
    SW_ASSERT(name);

    static bool isBuildCube = false; 
    if (!isBuildCube)
    {
        BuildCube(cubeSize);
        isBuildCube = true;
    }

    Add3dImpl(this, name, size, pos, axes, TYPE_Cube);
}

void SwDrawList::AddCone(const char* name, const float length, const float radius, const int slices, const float size, const SwVec2& pos, SwVec4& axes)
{
    SW_ASSERT(name);

    static bool isBuildCone = false; 
    if (!isBuildCone)
    {
        const float arrowBgn = -1.0f, arrowEnd = 1.0f;
        BuildCone(arrowEnd - length, arrowEnd, radius, slices);
        isBuildCone = true;
    }

    Add3dImpl(this, name, size, pos, axes, TYPE_Cone);
}

#endif // #ifndef SW_DISABLE
